﻿using Kernelx.Common.Clauses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Kernelx.Extensions._Internals.Clauses
{
    internal class Expression2ClauseElement
    {

        internal static IClauseElement GetClauseElement(Expression expr)
        {
            if (expr != null)
            {
                if (expr.NodeType == ExpressionType.AndAlso || expr.NodeType == ExpressionType.OrElse)
                {
                    var set = expr as BinaryExpression;
                    var left = GetClauseElement(set.Left);
                    var merge = expr.NodeType == ExpressionType.AndAlso ? ClauseMerge.And : ClauseMerge.Or;
                    var right = GetClauseElement(set.Right);
                    return new MergeClauseElement { Left = left, Merge = merge, Right = right };
                }
                else
                {
                    var propName = GetPropName(expr);
                    var @operator = GetOperator(expr);
                    var value = GetValue(expr);
                    return new ClauseElement { PropName = propName, Operator = @operator, Value = value };
                }
            }
            return null;
        }

        private static string GetPropName(MemberExpression member)
        {
            var name = member.Member.Name;
            if (member.Expression is MemberExpression ma)
                name = $"{GetPropName(ma)}.{name}";
            return name;
        }

        private static string GetPropName(Expression expr)
        {
            if (expr.NodeType == ExpressionType.Not)
                expr = (expr as UnaryExpression).Operand;

            if (expr.NodeType == ExpressionType.Call)
            {
                var mc = expr as MethodCallExpression;
                if (mc.Object != null && mc.Object.NodeType == ExpressionType.MemberAccess)
                    return GetPropName(mc.Object as MemberExpression);


                if (mc.Arguments.FirstOrDefault(x => x.NodeType == ExpressionType.MemberAccess) is MemberExpression ma)
                    return GetPropName(ma);

            }
            if (expr is BinaryExpression binary)
            {
                if (binary.Left.NodeType == ExpressionType.MemberAccess)
                    return GetPropName(binary.Left as MemberExpression);
                if (binary.Right.NodeType == ExpressionType.MemberAccess)
                    return GetPropName(binary.Right as MemberExpression);
            }
            return string.Empty;
        }


        enum SupportMethodName
        {
            Contains,
            Equals,
            StartsWith,
            EndsWith, 
        }


        private static readonly IList<string> _supportMethodNames = Enum.GetNames(typeof(SupportMethodName));

        private static ClauseOperator GetOperator(Expression expr)
        {
            bool isNot;
            if (isNot = expr.NodeType == ExpressionType.Not)
                expr = (expr as UnaryExpression).Operand;
            if (expr.NodeType == ExpressionType.Call)
            {
                var mcmName = (expr as MethodCallExpression).Method.Name;
                if (!_supportMethodNames.Contains(mcmName))
                    throw new ApplicationException($"暂不支持{mcmName}方法");
                if (isNot && mcmName != SupportMethodName.Contains.ToString())
                    throw new ApplicationException("! 仅允许加在Constains操作前面");
                 

                if (isNot && mcmName == SupportMethodName.Contains.ToString())
                    return ClauseOperator.NotContains;



                return  (ClauseOperator)Enum.Parse(typeof(ClauseOperator), mcmName);                 
            }

            if (expr.NodeType == ExpressionType.Equal)
                return ClauseOperator.Equal;
            if (expr.NodeType == ExpressionType.NotEqual)
                return ClauseOperator.NotEqual;
            if (expr.NodeType == ExpressionType.GreaterThan)
                return ClauseOperator.GreaterThan;
            if (expr.NodeType == ExpressionType.LessThan)
                return ClauseOperator.LessThan;
            if (expr.NodeType == ExpressionType.GreaterThanOrEqual)
                return ClauseOperator.ThanOrEqual;
            if (expr.NodeType == ExpressionType.LessThanOrEqual)
                return ClauseOperator.LessThanOrEqual;

            return ClauseOperator.Equal;
        }
        private static object GetValue(Expression expr)
        {
            if (expr.NodeType == ExpressionType.Not)
                expr = (expr as UnaryExpression).Operand;

            if (expr.NodeType == ExpressionType.Call)
            {
                var mc = expr as MethodCallExpression;

                if (mc.Arguments.FirstOrDefault(x => x.NodeType == ExpressionType.Constant) is ConstantExpression constant)
                    return constant.Value;
                if (mc.Arguments.FirstOrDefault(x => x.NodeType == ExpressionType.NewArrayInit) is NewArrayExpression arr)
                    return arr.Expressions.Select(x => (x as ConstantExpression).Value).ToList();
            }
            if (expr is BinaryExpression binary)
            {
                if (binary.Left.NodeType == ExpressionType.Constant)
                    return (binary.Left as ConstantExpression).Value;
                if (binary.Right.NodeType == ExpressionType.Constant)
                    return (binary.Right as ConstantExpression).Value;
            }
            return null;
        }

    }
}
